# Check requisite packages are installed.
packages <- c(
  "plotly", 
  "dplyr"
)
for (pkg in packages) {
  library(pkg, character.only = TRUE)
}
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5Loading required package: ggplot2
package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱plotly㤼㸲

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout

package 㤼㸱dplyr㤼㸲 was built under R version 4.0.4
Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union

Load

Pulling code almost directly from LM1996-NumPoolComScaling-Results-2021-05.Rmd.

dirViking <- c(
  file.path(
    getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling"
  ),
  file.path(
    getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling2"
  )
)
dirVikingResults <- file.path(
  dirViking, c("results-2021-04", "save-2021-05-10") # Latter not 100% yet.
)
resultFormat <- paste0(
  "run-", 
  "%d", # Combination Number, or CombnNum.
  "-", 
  "%s", # Run Seed.
  ".RDS"
)

2021-04 Data

# Copied from LawMorton1996-NumericalPoolCommunityScaling-Calculation.R
set.seed(38427042)

basal <- c(3, 10, 30, 100, 300, 1000)
consumer <- c(3, 10, 30, 100, 300, 1000) * 2
events <- (max(basal) + max(consumer)) * 2
runs <- 100

logBodySize <- c(-2, -1, -1, 1) # Morton and Law 1997 version.
parameters <- c(0.01, 10, 0.5, 0.2, 100, 0.1)

# Need to rerun seedsPrep to get the random number generation right for seedsRun
seedsPrep <- runif(2 * length(basal) * length(consumer)) * 1E8
seedsRun <- runif(runs * length(basal) * length(consumer)) * 1E8
paramFrame <- with(list(
  b = rep(basal, times = length(consumer)),
  c = rep(consumer, each = length(basal)),
  s1 = seedsPrep[1:(length(basal) * length(consumer))],
  s2 = seedsPrep[
    (length(basal) * length(consumer) + 1):(
      2 * length(basal) * length(consumer))
  ],
  sR = seedsRun
), {
  temp <- data.frame(
    CombnNum = 0,
    Basals = b,
    Consumers = c,
    SeedPool = s1,
    SeedMat = s2,
    SeedRuns = "",
    SeedRunsNum = 0,
    EndStates = I(rep(list(""), length(b))),
    EndStatesNum = 0,
    EndStateSizes = I(rep(list(""), length(b))),
    EndStateSizesNum = NA,
    EndStateAssembly = I(rep(list(""), length(b))),
    EndStateAbundance = I(rep(list(""), length(b))),
    Dataset = "2021-04",
    DatasetID = 1,
    stringsAsFactors = FALSE
  )
  for (i in 1:nrow(temp)) {
    seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
    temp$SeedRuns[i] <- toString(seeds) # CSV
    temp$SeedRunsNum[i] <- length(seeds)
  }
  temp$CombnNum <- 1:nrow(temp)
  temp
})
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
for (i in 1:nrow(paramFrame)) {
  resultsList <- list(
    "No Run" = 0,
    "No State" = 0
  )
  resultsSize <- list(
    "0" = 0
  )
  resultsAssembly <- list(
    "No Run" = data.frame(),
    "No State" = data.frame()
  )
  seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
  for (seed in seeds) {
    fileName <- file.path(
      dirVikingResults[paramFrame$DatasetID[i]],
      sprintf(resultFormat, paramFrame$CombnNum[i], seed)
    )
    
    if (file.exists(fileName)) {
      temp <- load(fileName)
      temp <- eval(parse(text = temp)) # Get objects.
      
      if (is.data.frame(temp)) {
        community <- toString(
          temp[[ncol(temp)]][[nrow(temp)]]
        )
        size <- toString(length(temp[[ncol(temp)]][[nrow(temp)]]))
        
        if (community == "") {
          resultsList$`No State` <- resultsList$`No State` + 1
          resultsSize$`0` <- resultsSize$`0` + 1
          
        } else if (community %in% names(resultsList)) {
          resultsList[[community]] <- resultsList[[community]] + 1
          resultsSize[[size]] <- resultsSize[[size]] + 1
          
        } else {
          resultsList[[community]] <- 1
          resultsAssembly[[community]] <- temp
          
          if (size %in% resultsSize) {
            resultsSize[[size]] <- resultsSize[[size]] + 1
          } else {
            resultsSize[[size]] <- 1
          }
        }
      } else {
        resultsList$`No State` <- resultsList$`No State` + 1
        resultsSize$`0` <- resultsSize$`0` + 1
      }
    } else {
      resultsList$`No Run` <- resultsList$`No Run` + 1
      resultsSize$`0` <- resultsSize$`0` + 1
    }
  }
  
  paramFrame$EndStates[[i]] <- resultsList
  paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize
  paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
  paramFrame$EndStateAssembly[[i]] <- resultsAssembly
}

2021-05 Data

source(
  file.path(getwd(), 
            "LawMorton1996-NumericalPoolCommunityScaling-Settings2.R")
)

oldNrow <- nrow(paramFrame)

paramFrame <- rbind(paramFrame, with(list(
  b = rep(basal, times = length(consumer)),
  c = rep(consumer, each = length(basal)),
  s1 = seedsPrep[1:(length(basal) * length(consumer))],
  s2 = seedsPrep[
    (length(basal) * length(consumer) + 1):(
      2 * length(basal) * length(consumer))
  ],
  sR = seedsRun
), {
  temp <- data.frame(
    CombnNum = 0,
    Basals = b,
    Consumers = c,
    SeedPool = s1,
    SeedMat = s2,
    SeedRuns = "",
    SeedRunsNum = 0,
    EndStates = I(rep(list(""), length(b))),
    EndStatesNum = 0,
    EndStateSizes = I(rep(list(""), length(b))),
    EndStateSizesNum = NA,
    EndStateAssembly = I(rep(list(""), length(b))),
    EndStateAbundance = I(rep(list(""), length(b))),
    Dataset = "2021-05",
    DatasetID = 2,
    stringsAsFactors = FALSE
  )
  for (i in 1:nrow(temp)) {
    seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
    temp$SeedRuns[i] <- toString(seeds) # CSV
    temp$SeedRunsNum[i] <- length(seeds)
  }
  temp$CombnNum <- 1:nrow(temp)
  temp
})
)
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
# Modified from above, but with the abundance recorded.
for (i in (oldNrow + 1):nrow(paramFrame)) {
  resultsList <- list(
    "No Run" = 0,
    "No State" = 0
  )
  resultsSize <- list(
    "0" = 0
  )
  resultsAssembly <- list(
    "No Run" = data.frame(),
    "No State" = data.frame()
  )
  resultsAbund <- list(
    "No Run" = "",
    "No State" = ""
  )
  seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
  for (seed in seeds) {
    fileName <- file.path(
      dirVikingResults[paramFrame$DatasetID[i]],
      sprintf(resultFormat, paramFrame$CombnNum[i], seed)
    )
    
    if (file.exists(fileName)) {
      temp <- load(fileName)
      temp <- eval(parse(text = temp)) # Get objects.
      
      if (is.list(temp) && "Result" %in% names(temp)) {
        
        if (is.data.frame(temp$Result))
          community <- temp$Result$Community[[nrow(temp$Result)]]
        else 
          community <- temp$Result
        
        size <- toString(length(community))
        
        if (community[1] != "") 
          abund <- toString(temp$Abund[community + 1])
        else 
          abund <- ""
        
        community <- toString(community)
        
        if (community == "") {
          resultsList$`No State` <- resultsList$`No State` + 1
          resultsSize$`0` <- resultsSize$`0` + 1
          
        } else if (community %in% names(resultsList)) {
          resultsList[[community]] <- resultsList[[community]] + 1
          resultsSize[[size]] <- resultsSize[[size]] + 1
          
        } else {
          resultsList[[community]] <- 1
          resultsAssembly[[community]] <- temp
          resultsAbund[[community]] <- abund
          
          if (size %in% resultsSize) {
            resultsSize[[size]] <- resultsSize[[size]] + 1
          } else {
            resultsSize[[size]] <- 1
          }
        }
      } else {
        resultsList$`No State` <- resultsList$`No State` + 1
        resultsSize$`0` <- resultsSize$`0` + 1
      }
    } else {
      resultsList$`No Run` <- resultsList$`No Run` + 1
      resultsSize$`0` <- resultsSize$`0` + 1
    }
  }
  
  paramFrame$EndStates[[i]] <- resultsList
  paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize
  paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
  paramFrame$EndStateAssembly[[i]] <- resultsAssembly
  paramFrame$EndStateAbundance[[i]] <- resultsAbund
}

Test Data

testRowNums <- nrow(paramFrame)
testRowsToAdd <- c(2, 6) # Make sure to put in numerical order!

paramFrame <- with(
  list(
    basal2 = c(5, 10, 15),
    consumer2 = c(20, 40, 60),
    logBodySize = c(-2, -1, -1, 0),
    parameters = c(0.01, 10, 0.5, 0.2, 100, 0.1)
  ),
  {
    set.seed(3680180)
    seedsPrep2 <- runif(2 * length(basal2) * length(consumer2)) * 1E8
    with(list(
      b = rep(basal2, times = length(consumer2)),
      c = rep(consumer2, each = length(basal2)),
      s1 = seedsPrep2[1:(length(basal2) * length(consumer2))],
      s2 = seedsPrep2[
        (length(basal2) * length(consumer2) + 1):(
          2 * length(basal2) * length(consumer2))
      ]
    ), {
      rbind(
        paramFrame,
        data.frame(
          CombnNum = testRowsToAdd,
          Basals = b[testRowsToAdd],
          Consumers = c[testRowsToAdd],
          SeedPool = s1[testRowsToAdd],
          SeedMat = s2[testRowsToAdd],
          SeedRuns = "",
          SeedRunsNum = 0,
          EndStates = I(rep(list(""), length(testRowsToAdd))),
          EndStatesNum = 0,
          EndStateSizes = I(rep(list(""), length(testRowsToAdd))),
          EndStateSizesNum = NA,
          EndStateAssembly = I(rep(list(""), length(testRowsToAdd))),
          EndStateAbundance = I(rep(list(""), length(testRowsToAdd))),
          Dataset = "Test",
          DatasetID = max(paramFrame$DatasetID) + 1,
          stringsAsFactors = FALSE
        )
      )
    }
    )
  }
)

testRowNums <- (testRowNums + 1):nrow(paramFrame)
resultsList <- list(
  list(
    "No Run" = 0,
    "No State" = 0,
    "2, 4, 6, 12, 29" = 1,
    "2, 4, 6, 13, 29" = 1
  ),
  list(
    "No Run" = 0,
    "No State" = 0,
    "8, 10, 12, 14, 15, 16, 39, 43" = 1,
    "8, 12, 14, 15, 16, 38, 39" = 1
  )
)
resultsSize <- list(
  list(
    "0" = 0,
    "5" = 2
  ),
  list(
    "0" = 0,
    "8" = 1,
    "7" = 1
  )
)
resultsAbund <- list(
  list(
    "No Run" = "",
    "No State" = "",
    "2, 4, 6, 12, 29" = "742.88553671712, 80.579233072626, 162.128399850253, 20.2082198699389, 18.8589490510429",
    "2, 4, 6, 13, 29" = "668.664143581837, 119.024146851052, 127.680269383867, 30.657960866033, 13.4844194707944"
  ),
  list(
    "No Run" = "",
    "No State" = "",
    "8, 10, 12, 14, 15, 16, 39, 43" = "20.7665807606491, 32.4461165261454, 80.4033387818895, 817.879722033354, 121.136570782828, 18.0390671088957, 12.3834561177271, 19.9674718543196",
    "8, 12, 14, 15, 16, 38, 39" = "82.592048492812, 138.267379166014, 938.158436379166, 51.8610963745021, 5.03556251837491, 14.1019343145825, 25.9231062711228"
  )
)

for (j in seq_along(testRowNums)) {
  i <- testRowNums[j]
  paramFrame$EndStates[[i]] <- resultsList[[j]]
  paramFrame$EndStatesNum[i] <- length(resultsList[[j]]) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize[[j]]
  paramFrame$EndStateSizesNum[i] <- length(resultsSize[[j]]) - 1 # ! 0
  paramFrame$EndStateAbundance[[i]] <- resultsAbund[[j]]
}

Plot

# X, Y, Basal and Consumer.
# Z = Sizes of the Endstates.

plotScalingData <- data.frame(
  CombnNum = rep(paramFrame$CombnNum, paramFrame$EndStatesNum),
  Basals = rep(paramFrame$Basals, paramFrame$EndStatesNum),
  Consumers = rep(paramFrame$Consumers, paramFrame$EndStatesNum),
  Dataset = rep(paramFrame$Dataset, paramFrame$EndStatesNum),
  DatasetID = rep(paramFrame$DatasetID, paramFrame$EndStatesNum)
)

# Communities
comms <- unlist(lapply(paramFrame$EndStates, names))
freqs <- unlist(paramFrame$EndStates)
asmbl <- unlist(paramFrame$EndStateAssembly, recursive = FALSE)
asmbl <- asmbl[comms != "No Run" & comms != "No State"]
freqs <- freqs[comms != "No Run" & comms != "No State"]
comms <- comms[comms != "No Run" & comms != "No State"]

asmbl <- lapply(asmbl, function(d) {
  if (is.null(d)) return(NA)
  if ("Result.Outcome" %in% names(d))
    d %>% dplyr::filter(Result.Outcome != "Type 1 (Failure)" & 
                          Result.Outcome != "Present")
  else
    d$Result %>% dplyr::filter(Outcome != "Type 1 (Failure)" & 
                                 Outcome != "Present")
})

plotScalingData$Communities <- comms
plotScalingData$CommunityFreq <- freqs
plotScalingData$CommunitySeq <- asmbl

# Community Size
temp <- unlist(lapply(strsplit(plotScalingData$Communities, ','), length))
plotScalingData$CommunitySize <- temp

# For usage by the reader.

plotScaling <- plotly::plot_ly(
  plotScalingData,
  x = ~Basals,
  y = ~Consumers,
  z = ~CommunitySize,
  color = ~Dataset,
  colors = c("red", "blue", "black")
)

plotScaling <- plotly::add_markers(plotScaling)

plotScaling <- plotly::layout(
  plotScaling,
  scene = list(
    xaxis = list(type = "log"),
    yaxis = list(type = "log"),
    camera = list(
      eye = list(
        x = -1.25, y = -1.25, z = .05
      )
    )
  )
)

plotScaling

Abundances

# > runif(1) * 1E8
# [1] 82598679
set.seed(82598679)

mats <- list()
poolsall <- list() # name pools used in save data; be careful!

for (i in 1:length(dirViking)) {
  temp <- load(file.path(
    dirViking[i], 
    paste0("LawMorton1996-NumericalPoolCommunityScaling-PoolMats", 
           if (i > 1) i else "", 
           ".RDS")
  ))
  mats[[i]] <- eval(parse(text = temp[1]))
  poolsall[[i]] <- eval(parse(text = temp[2]))
}
pools <- poolsall

# Add in the test datasets.
poolsTemp <- list()
matsTemp <- list()
for (r in testRowNums) {
  testRowRow <- paramFrame[r, ]
  poolsTemp[[testRowRow$CombnNum]] <- with(testRowRow,
    RMTRCode2::LawMorton1996_species(
      Basal = Basals,
      Consumer = Consumers,
      Parameters = c(0.01, 10, 0.5, 0.2, 100, 0.1),
      LogBodySize = c(-2, -1, -1, 0),
      seed = SeedPool
    )
  )
  matsTemp[[testRowRow$CombnNum]] <- with(testRowRow,
    RMTRCode2::LawMorton1996_CommunityMat(
      Pool = poolsTemp[[CombnNum]],
      Parameters = c(0.01, 10, 0.5, 0.2, 100, 0.1),
      seed = SeedMat
    )
  )
}
executing %dopar% sequentially: no parallel backend registered
pools[[i + 1]] <- poolsTemp
mats[[i + 1]] <- matsTemp

oldCandidateData <- load(file.path(getwd(), "candidateDataSoFar.Rdata"))
oldCandidateData <- eval(parse(text = oldCandidateData))
candidateData <- plotScalingData %>% dplyr::group_by(
  CombnNum, Dataset
) %>% dplyr::mutate(
  OtherSteadyStates = dplyr::n() - 1
) %>% dplyr::filter(
  OtherSteadyStates > 0
)
candidateData %>% dplyr::select(-CommunitySeq)
# First, check if it is in the paramFrame.
# Second, check if it is in the saved data from the previous.
# Otherwise, ignore it, we'll figure out what it is and why it is missing later.

candidateData$CommunityAbund <- ""

for (r in 1:nrow(candidateData)) {
  # ID 1:4 are used to identify paramFrame, 5 used to identify abundance
  ID <- candidateData[r, 1:6]
  paramFrameRow <- paramFrame %>% dplyr::filter(
    CombnNum == ID$CombnNum,
    Basals == ID$Basals,
    Consumers == ID$Consumers,
    Dataset == ID$Dataset
  )
  
  if (is.list(paramFrameRow$EndStateAbundance[[1]])) {
    entry <- which(ID$Communities == names(paramFrameRow$EndStateAbundance[[1]]))
    if (length(entry)) {
      candidateData$CommunityAbund[r] <- paramFrameRow$EndStateAbundance[[1]][[entry]]
      next()
    }
  }
  
  if (ID$Dataset == "2021-04") {
    
    oldCandDatRow <- oldCandidateData %>% dplyr::filter(
      CombnNum == ID$CombnNum,
      Basals == ID$Basals,
      Consumers == ID$Consumers,
      Communities == ID$Communities
    )
    
    if (nrow(oldCandDatRow) > 0) {
      if (oldCandDatRow$CommunityAbund != "") {
        candidateData$CommunityAbund[r] <- oldCandDatRow$CommunityAbund
      }
    }
  }
}
for (r in 1:nrow(candidateData)) {
  if (!(candidateData$CommunityAbund[r] == "Failure" |
      candidateData$CommunityAbund[r] == "")) next

  # Random guesses, starting from structured.
  temp <- with(
    candidateData[r, ],
    RMTRCode2::FindSteadyStateFromEstimate(
      Pool = pools[[DatasetID]][[CombnNum]],
      InteractionMatrix = mats[[DatasetID]][[CombnNum]],
      Community = Communities,
      Populations = ifelse(
        pools[[DatasetID]][[CombnNum]]$Type[
          RMTRCode2::CsvRowSplit(Communities)
        ] == "Basal",
        1000, 10)
    )
  )

  if (any(temp) < 1E-4) {
    temp <- "EstimateFailure"
  } else {
    temp <- toString(temp)
  }
  candidateData$CommunityAbund[r] <- temp
}
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2843.06
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2876.33
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2870.65
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2873.23
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2871.69
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they gocoercing argument of type 'double' to logicalcoercing argument of type 'double' to logicalcoercing argument of type 'double' to logicalcoercing argument of type 'double' to logicalcoercing argument of type 'double' to logicalcoercing argument of type 'double' to logical
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 6597.38
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they gocoercing argument of type 'double' to logical
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3592.76
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3780.53
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3786.89
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3784.07
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3778.43
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3777.75
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3774.98
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3781.84
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they gocoercing argument of type 'double' to logical
candidateData <- candidateData %>% dplyr::filter(CommunityAbund != "",
                                                 CommunityAbund != "Failure",
                                                 CommunityAbund != "EstimateFailure")
candidateData$CommunityProd <- NA
for (r in 1:nrow(candidateData)) {
  candidateData$CommunityProd[r] <- with(
    candidateData[r, ], 
    RMTRCode2::Productivity(
      Pool = pools[[DatasetID]][[CombnNum]], 
      InteractionMatrix = mats[[DatasetID]][[CombnNum]], 
      Community = Communities, 
      Populations = CommunityAbund
    )
  )
}

Simple Island Results

islandFUN <- function(i, dat, pool, mat, dmat) {
  temp <- dat[i, ]
  RMTRCode2::IslandDynamics(
    Pool = pool,
    InteractionMatrix = mat,
    Communities = c(
      list(temp$Communities[1]),
      rep("", nrow(dmat) - 2),
      temp$Communities[2]
    ),
    Populations = c(
      list(temp$CommunityAbund[1]),
      rep("", nrow(dmat) - 2),
      list(temp$CommunityAbund[2])
    ),
    DispersalPool = 0.0001,
    DispersalIsland = dmat,
    Verbose = FALSE
  )
}
# For each group-dataset,
# For each pair,
# Run Island Dynamics,
# Save the result with its pairing
candidateData$TotalID <- paste(candidateData$CombnNum, candidateData$DatasetID)

islandInteractionsOneTwo <- list()

for (grp in unique(candidateData$TotalID)) {
  candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
  
  if (nrow(candidateDataSubset) == 1) next()
  
  pairingResults <- combn(
    nrow(candidateDataSubset), 2, 
    islandFUN,
    dat = candidateDataSubset, 
    pool = pools[[
      candidateDataSubset$DatasetID[1]
    ]][[candidateDataSubset$CombnNum[1]]],
    mat = mats[[
      candidateDataSubset$DatasetID[1]
    ]][[candidateDataSubset$CombnNum[1]]],
    dmat = matrix(c(0, 1, 1, 0), nrow = 2, ncol = 2),
    simplify = FALSE
  )
  
  pairingResults <- lapply(
    pairingResults, function(mat, isles) {
      mat <- mat[nrow(mat), -1]
      retVal <- list()
      species <- length(mat) / isles
      for (i in 1:isles) {
        retVal[[i]] <- mat[((i - 1) * species + 1) : (i * species)]
      }
      retVal
    },
    isles = 2
  )
  
  islandInteractionsOneTwo[[grp]] <- pairingResults
}
islandInteractionsOneEmptyTwo <- list()

for (grp in unique(candidateData$TotalID)) {
  candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
  
  if (nrow(candidateDataSubset) == 1) next()
  
  pairingResults <- combn(
    nrow(candidateDataSubset), 2, 
    islandFUN,
    dat = candidateDataSubset, 
    pool = pools[[
      candidateDataSubset$DatasetID[1]
    ]][[candidateDataSubset$CombnNum[1]]],
    mat = mats[[
      candidateDataSubset$DatasetID[1]
    ]][[candidateDataSubset$CombnNum[1]]],
    dmat = matrix(c(
      0, 1, 0, # Island 2 -> 1
      1, 0, 1, # Island 1 -> 2, Island 3 -> 2
      0, 1, 0  # Island 2 -> 3
    ), nrow = 3, ncol = 3, byrow = TRUE),
    simplify = FALSE
  )
  
  pairingResults <- lapply(
    pairingResults, function(mat, isles) {
      mat <- mat[nrow(mat), -1]
      retVal <- list()
      species <- length(mat) / isles
      for (i in 1:isles) {
        retVal[[i]] <- mat[((i - 1) * species + 1) : (i * species)]
      }
      retVal
    },
    isles = 3
  )
  
  islandInteractionsOneEmptyTwo[[grp]] <- pairingResults
}
# Format of table should be:
# ID, Community 1, Community 2, Outcomes 1-2, Outcomes 1-0-2
# For outcomes, species presence will be used.

# communities <- NULL
# totalCommunities <- NULL
# for (grp in unique(candidateData$TotalID)) {
#   candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
#   
#   if (nrow(candidateDataSubset) > 1) {
#     newCommunities <- combn(
#       candidateDataSubset$Communities, 2, 
#     )
#     communities <- c(communities, newCommunities)
#     totalCommunities <- c(
#       totalCommunities,
#       toString(sort(unique(unlist(lapply(newCommunities, 
#                                          RMTRCode2::CsvRowSplit)))))
#     )
#   }
# }

communities <- NULL
totalCommunities <- NULL
for (grp in unique(candidateData$TotalID)) {
  candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
  
  if (nrow(candidateDataSubset) > 1) {
    newCommunities <- combn(
      candidateDataSubset$Communities, 2, 
    )
    communities <- c(communities, newCommunities)
    totalCommunities <- c( # Labelling is wrong here. Need tC from pairs of nC.
      totalCommunities,
      list(apply(newCommunities, 2, function(coms) {
        toString(sort(unique(RMTRCode2::CsvRowSplit(coms))))
      }))
    )
  }
}

minThresh <- 1E-6
islandInteractionsOneTwoWhich <- unlist(lapply(
    seq_along(islandInteractionsOneTwo), function(i, byID, communities) {
        lapply(seq_along(byID[[i]]), function(j, system, comms) {
            lapply(system[[j]], function(pipeEnd, comms) {
                toString(RMTRCode2::CsvRowSplit(comms)[
                    which(pipeEnd > minThresh)
                ])
            }, comms = comms[[j]])
        }, system = byID[[i]], comms = communities[[i]])
    }, byID = islandInteractionsOneTwo, communities = totalCommunities
))

islandInteractionsOneEmptyTwoWhich <- unlist(lapply(
    seq_along(islandInteractionsOneEmptyTwo), function(i, byID, communities) {
        lapply(seq_along(byID[[i]]), function(j, system, comms) {
            lapply(system[[j]], function(pipeEnd, comms) {
                toString(RMTRCode2::CsvRowSplit(comms)[
                    which(pipeEnd > minThresh)
                ])
            }, comms = comms[[j]])
        }, system = byID[[i]], comms = communities[[i]])
    }, byID = islandInteractionsOneEmptyTwo, communities = totalCommunities
))

# islandInteractionsOneTwoWhich <- unlist(lapply(
#   seq_along(islandInteractionsOneTwo), function(i, x, tC) {
#     lapply(x[[i]], function(y, tC) {
#       lapply(y, function(z, tC) {
#         toString(RMTRCode2::CsvRowSplit(tC)[which(z > 1E-6)])
#       }, tC = tC)
#     },
#     tC = tC[i])
#   },
#   x = islandInteractionsOneTwo,
#   tC = totalCommunities))
# 
# islandInteractionsOneEmptyTwoWhich <- unlist(lapply(
#   seq_along(islandInteractionsOneEmptyTwo), function(i, x, tC) {
#     lapply(x[[i]], function(y, tC) {
#       lapply(y, function(z, tC) {
#         toString(RMTRCode2::CsvRowSplit(tC)[which(z > 1E-6)])
#       }, tC = tC)
#     },
#     tC = tC[i])
#   },
#   x = islandInteractionsOneEmptyTwo,
#   tC = totalCommunities))

islandInteractionResults <- data.frame(
  DatasetID = rep(names(islandInteractionsOneTwo), 
                  unlist(lapply(islandInteractionsOneTwo, length))),
  Community1 = communities[seq(from = 1, to = length(communities), by = 2)],
  Community2 = communities[seq(from = 2, to = length(communities), by = 2)],
  OutcomeWOEmpty_Island1 = islandInteractionsOneTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneTwoWhich), by = 2)],
  OutcomeWOEmpty_Island2 = islandInteractionsOneTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneTwoWhich), by = 2)],
  OutcomeWEmpty_Island1 = islandInteractionsOneEmptyTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)],
  OutcomeWEmpty_Island2 = islandInteractionsOneEmptyTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)],
  OutcomeWEmpty_Island3 = islandInteractionsOneEmptyTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)]
)

islandInteractionResults

original outcomes before fix.

islandInteractionResults %>% dplyr::mutate(
  C1WOInvaded = Community1 != OutcomeWOEmpty_Island1,
  C2WOInvaded = Community2 != OutcomeWOEmpty_Island2,
  C1WInvaded = Community1 != OutcomeWEmpty_Island1,
  C2WInvaded = Community2 != OutcomeWEmpty_Island3,
  StalemateWO = !C1WOInvaded & !C2WOInvaded,
  StalemateW = !C1WInvaded & !C2WInvaded,
  HybridWO = C1WOInvaded & C2WOInvaded,
  HybridW = C1WInvaded & C2WInvaded,
) %>% dplyr::select(-dplyr::starts_with("Outcome"))

Outcomes after fix. Note the differences in the last column for easiest comparison.

islandInteractionResults %>% dplyr::mutate(
  C1WOInvaded = Community1 != OutcomeWOEmpty_Island1,
  C2WOInvaded = Community2 != OutcomeWOEmpty_Island2,
  C1WInvaded = Community1 != OutcomeWEmpty_Island1,
  C2WInvaded = Community2 != OutcomeWEmpty_Island3,
  StalemateWO = !C1WOInvaded & !C2WOInvaded,
  StalemateW = !C1WInvaded & !C2WInvaded,
  HybridWO = C1WOInvaded & C2WOInvaded,
  HybridW = C1WInvaded & C2WInvaded,
) %>% dplyr::select(-dplyr::starts_with("Outcome"))

Save workspace

save(
  candidateData,
  islandInteractionsOneEmptyTwo,
  islandInteractionsOneEmptyTwoWhich,
  islandInteractionsOneTwo,
  islandInteractionsOneTwoWhich,
  mats,
  paramFrame,
  plotScalingData,
  pools,
  file = "LM1996-NumPoolCom-QDat-2021-05.RData"
)
LS0tDQp0aXRsZTogIkFuc3dlcmluZyBRdWVzdGlvbnM7IEdhdGhlciBEYXRhLCAyMDIxLTA1Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCmBgYHtyIGxpYnN9DQojIENoZWNrIHJlcXVpc2l0ZSBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkLg0KcGFja2FnZXMgPC0gYygNCiAgInBsb3RseSIsIA0KICAiZHBseXIiDQopDQpmb3IgKHBrZyBpbiBwYWNrYWdlcykgew0KICBsaWJyYXJ5KHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KfQ0KYGBgDQoNCiMgTG9hZA0KUHVsbGluZyBjb2RlIGFsbW9zdCBkaXJlY3RseSBmcm9tIGBMTTE5OTYtTnVtUG9vbENvbVNjYWxpbmctUmVzdWx0cy0yMDIxLTA1LlJtZGAuDQpgYGB7ciBkaXJzfQ0KZGlyVmlraW5nIDwtIGMoDQogIGZpbGUucGF0aCgNCiAgICBnZXR3ZCgpLCAiTENBQl9MYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nIg0KICApLA0KICBmaWxlLnBhdGgoDQogICAgZ2V0d2QoKSwgIkxDQUJfTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZzIiDQogICkNCikNCmRpclZpa2luZ1Jlc3VsdHMgPC0gZmlsZS5wYXRoKA0KICBkaXJWaWtpbmcsIGMoInJlc3VsdHMtMjAyMS0wNCIsICJzYXZlLTIwMjEtMDUtMTAiKSAjIExhdHRlciBub3QgMTAwJSB5ZXQuDQopDQpyZXN1bHRGb3JtYXQgPC0gcGFzdGUwKA0KICAicnVuLSIsIA0KICAiJWQiLCAjIENvbWJpbmF0aW9uIE51bWJlciwgb3IgQ29tYm5OdW0uDQogICItIiwgDQogICIlcyIsICMgUnVuIFNlZWQuDQogICIuUkRTIg0KKQ0KYGBgDQoNCiMjIDIwMjEtMDQgRGF0YQ0KYGBge3IgcGFyYW1zfQ0KIyBDb3BpZWQgZnJvbSBMYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nLUNhbGN1bGF0aW9uLlINCnNldC5zZWVkKDM4NDI3MDQyKQ0KDQpiYXNhbCA8LSBjKDMsIDEwLCAzMCwgMTAwLCAzMDAsIDEwMDApDQpjb25zdW1lciA8LSBjKDMsIDEwLCAzMCwgMTAwLCAzMDAsIDEwMDApICogMg0KZXZlbnRzIDwtIChtYXgoYmFzYWwpICsgbWF4KGNvbnN1bWVyKSkgKiAyDQpydW5zIDwtIDEwMA0KDQpsb2dCb2R5U2l6ZSA8LSBjKC0yLCAtMSwgLTEsIDEpICMgTW9ydG9uIGFuZCBMYXcgMTk5NyB2ZXJzaW9uLg0KcGFyYW1ldGVycyA8LSBjKDAuMDEsIDEwLCAwLjUsIDAuMiwgMTAwLCAwLjEpDQoNCiMgTmVlZCB0byByZXJ1biBzZWVkc1ByZXAgdG8gZ2V0IHRoZSByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gcmlnaHQgZm9yIHNlZWRzUnVuDQpzZWVkc1ByZXAgPC0gcnVuaWYoMiAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKSAqIDFFOA0Kc2VlZHNSdW4gPC0gcnVuaWYocnVucyAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKSAqIDFFOA0KYGBgDQoNCmBgYHtyIG9yZ2FuaXNlUGFyYW1zfQ0KcGFyYW1GcmFtZSA8LSB3aXRoKGxpc3QoDQogIGIgPSByZXAoYmFzYWwsIHRpbWVzID0gbGVuZ3RoKGNvbnN1bWVyKSksDQogIGMgPSByZXAoY29uc3VtZXIsIGVhY2ggPSBsZW5ndGgoYmFzYWwpKSwNCiAgczEgPSBzZWVkc1ByZXBbMToobGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpXSwNCiAgczIgPSBzZWVkc1ByZXBbDQogICAgKGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpICsgMSk6KA0KICAgICAgMiAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKQ0KICBdLA0KICBzUiA9IHNlZWRzUnVuDQopLCB7DQogIHRlbXAgPC0gZGF0YS5mcmFtZSgNCiAgICBDb21ibk51bSA9IDAsDQogICAgQmFzYWxzID0gYiwNCiAgICBDb25zdW1lcnMgPSBjLA0KICAgIFNlZWRQb29sID0gczEsDQogICAgU2VlZE1hdCA9IHMyLA0KICAgIFNlZWRSdW5zID0gIiIsDQogICAgU2VlZFJ1bnNOdW0gPSAwLA0KICAgIEVuZFN0YXRlcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBFbmRTdGF0ZXNOdW0gPSAwLA0KICAgIEVuZFN0YXRlU2l6ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVTaXplc051bSA9IE5BLA0KICAgIEVuZFN0YXRlQXNzZW1ibHkgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVBYnVuZGFuY2UgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRGF0YXNldCA9ICIyMDIxLTA0IiwNCiAgICBEYXRhc2V0SUQgPSAxLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIGZvciAoaSBpbiAxOm5yb3codGVtcCkpIHsNCiAgICBzZWVkcyA8LSBzUlsoKGkgLSAxKSAqIHJ1bnMgKyAxKSA6IChpICogcnVucyldDQogICAgdGVtcCRTZWVkUnVuc1tpXSA8LSB0b1N0cmluZyhzZWVkcykgIyBDU1YNCiAgICB0ZW1wJFNlZWRSdW5zTnVtW2ldIDwtIGxlbmd0aChzZWVkcykNCiAgfQ0KICB0ZW1wJENvbWJuTnVtIDwtIDE6bnJvdyh0ZW1wKQ0KICB0ZW1wDQp9KQ0KYGBgDQoNCmBgYHtyIGxvYWRSZXN1bHRzfQ0KIyBOb3RlOiBuICsgMiBlbmQgc3RhdGVzLiBGYWlsdXJlIHRvIGZpbmlzaCwgZmFpbHVyZSB0byBvYnRhaW4gc3RhdGUsIGFuZCBzdGF0ZS4NCmZvciAoaSBpbiAxOm5yb3cocGFyYW1GcmFtZSkpIHsNCiAgcmVzdWx0c0xpc3QgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9IDAsDQogICAgIk5vIFN0YXRlIiA9IDANCiAgKQ0KICByZXN1bHRzU2l6ZSA8LSBsaXN0KA0KICAgICIwIiA9IDANCiAgKQ0KICByZXN1bHRzQXNzZW1ibHkgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9IGRhdGEuZnJhbWUoKSwNCiAgICAiTm8gU3RhdGUiID0gZGF0YS5mcmFtZSgpDQogICkNCiAgc2VlZHMgPC0gdW5saXN0KHN0cnNwbGl0KHBhcmFtRnJhbWUkU2VlZFJ1bnNbaV0sICcsICcpKQ0KICBmb3IgKHNlZWQgaW4gc2VlZHMpIHsNCiAgICBmaWxlTmFtZSA8LSBmaWxlLnBhdGgoDQogICAgICBkaXJWaWtpbmdSZXN1bHRzW3BhcmFtRnJhbWUkRGF0YXNldElEW2ldXSwNCiAgICAgIHNwcmludGYocmVzdWx0Rm9ybWF0LCBwYXJhbUZyYW1lJENvbWJuTnVtW2ldLCBzZWVkKQ0KICAgICkNCiAgICANCiAgICBpZiAoZmlsZS5leGlzdHMoZmlsZU5hbWUpKSB7DQogICAgICB0ZW1wIDwtIGxvYWQoZmlsZU5hbWUpDQogICAgICB0ZW1wIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXApKSAjIEdldCBvYmplY3RzLg0KICAgICAgDQogICAgICBpZiAoaXMuZGF0YS5mcmFtZSh0ZW1wKSkgew0KICAgICAgICBjb21tdW5pdHkgPC0gdG9TdHJpbmcoDQogICAgICAgICAgdGVtcFtbbmNvbCh0ZW1wKV1dW1tucm93KHRlbXApXV0NCiAgICAgICAgKQ0KICAgICAgICBzaXplIDwtIHRvU3RyaW5nKGxlbmd0aCh0ZW1wW1tuY29sKHRlbXApXV1bW25yb3codGVtcCldXSkpDQogICAgICAgIA0KICAgICAgICBpZiAoY29tbXVuaXR5ID09ICIiKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCA8LSByZXN1bHRzTGlzdCRgTm8gU3RhdGVgICsgMQ0KICAgICAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSBpZiAoY29tbXVuaXR5ICVpbiUgbmFtZXMocmVzdWx0c0xpc3QpKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIHJlc3VsdHNTaXplW1tzaXplXV0gKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIDENCiAgICAgICAgICByZXN1bHRzQXNzZW1ibHlbW2NvbW11bml0eV1dIDwtIHRlbXANCiAgICAgICAgICANCiAgICAgICAgICBpZiAoc2l6ZSAlaW4lIHJlc3VsdHNTaXplKSB7DQogICAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIHJlc3VsdHNTaXplW1tzaXplXV0gKyAxDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIHJlc3VsdHNTaXplW1tzaXplXV0gPC0gMQ0KICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCA8LSByZXN1bHRzTGlzdCRgTm8gU3RhdGVgICsgMQ0KICAgICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgICAgfQ0KICAgIH0gZWxzZSB7DQogICAgICByZXN1bHRzTGlzdCRgTm8gUnVuYCA8LSByZXN1bHRzTGlzdCRgTm8gUnVuYCArIDENCiAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgfQ0KICB9DQogIA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc1tbaV1dIDwtIHJlc3VsdHNMaXN0DQogIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtW2ldIDwtIGxlbmd0aChyZXN1bHRzTGlzdCkgLSAyICMgISBObyBTdGF0ZSwgTm8gUnVuDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVTaXplc1tbaV1dIDwtIHJlc3VsdHNTaXplDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVTaXplc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c1NpemUpIC0gMSAjICEgMA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlQXNzZW1ibHlbW2ldXSA8LSByZXN1bHRzQXNzZW1ibHkNCn0NCmBgYA0KDQojIyAyMDIxLTA1IERhdGENCmBgYHtyIG9yZ2FuaXNlUGFyYW1zMn0NCnNvdXJjZSgNCiAgZmlsZS5wYXRoKGdldHdkKCksIA0KICAgICAgICAgICAgIkxhd01vcnRvbjE5OTYtTnVtZXJpY2FsUG9vbENvbW11bml0eVNjYWxpbmctU2V0dGluZ3MyLlIiKQ0KKQ0KDQpvbGROcm93IDwtIG5yb3cocGFyYW1GcmFtZSkNCg0KcGFyYW1GcmFtZSA8LSByYmluZChwYXJhbUZyYW1lLCB3aXRoKGxpc3QoDQogIGIgPSByZXAoYmFzYWwsIHRpbWVzID0gbGVuZ3RoKGNvbnN1bWVyKSksDQogIGMgPSByZXAoY29uc3VtZXIsIGVhY2ggPSBsZW5ndGgoYmFzYWwpKSwNCiAgczEgPSBzZWVkc1ByZXBbMToobGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpXSwNCiAgczIgPSBzZWVkc1ByZXBbDQogICAgKGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpICsgMSk6KA0KICAgICAgMiAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKQ0KICBdLA0KICBzUiA9IHNlZWRzUnVuDQopLCB7DQogIHRlbXAgPC0gZGF0YS5mcmFtZSgNCiAgICBDb21ibk51bSA9IDAsDQogICAgQmFzYWxzID0gYiwNCiAgICBDb25zdW1lcnMgPSBjLA0KICAgIFNlZWRQb29sID0gczEsDQogICAgU2VlZE1hdCA9IHMyLA0KICAgIFNlZWRSdW5zID0gIiIsDQogICAgU2VlZFJ1bnNOdW0gPSAwLA0KICAgIEVuZFN0YXRlcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBFbmRTdGF0ZXNOdW0gPSAwLA0KICAgIEVuZFN0YXRlU2l6ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVTaXplc051bSA9IE5BLA0KICAgIEVuZFN0YXRlQXNzZW1ibHkgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVBYnVuZGFuY2UgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRGF0YXNldCA9ICIyMDIxLTA1IiwNCiAgICBEYXRhc2V0SUQgPSAyLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIGZvciAoaSBpbiAxOm5yb3codGVtcCkpIHsNCiAgICBzZWVkcyA8LSBzUlsoKGkgLSAxKSAqIHJ1bnMgKyAxKSA6IChpICogcnVucyldDQogICAgdGVtcCRTZWVkUnVuc1tpXSA8LSB0b1N0cmluZyhzZWVkcykgIyBDU1YNCiAgICB0ZW1wJFNlZWRSdW5zTnVtW2ldIDwtIGxlbmd0aChzZWVkcykNCiAgfQ0KICB0ZW1wJENvbWJuTnVtIDwtIDE6bnJvdyh0ZW1wKQ0KICB0ZW1wDQp9KQ0KKQ0KYGBgDQoNCmBgYHtyIGxvYWRSZXN1bHRzMn0NCiMgTm90ZTogbiArIDIgZW5kIHN0YXRlcy4gRmFpbHVyZSB0byBmaW5pc2gsIGZhaWx1cmUgdG8gb2J0YWluIHN0YXRlLCBhbmQgc3RhdGUuDQojIE1vZGlmaWVkIGZyb20gYWJvdmUsIGJ1dCB3aXRoIHRoZSBhYnVuZGFuY2UgcmVjb3JkZWQuDQpmb3IgKGkgaW4gKG9sZE5yb3cgKyAxKTpucm93KHBhcmFtRnJhbWUpKSB7DQogIHJlc3VsdHNMaXN0IDwtIGxpc3QoDQogICAgIk5vIFJ1biIgPSAwLA0KICAgICJObyBTdGF0ZSIgPSAwDQogICkNCiAgcmVzdWx0c1NpemUgPC0gbGlzdCgNCiAgICAiMCIgPSAwDQogICkNCiAgcmVzdWx0c0Fzc2VtYmx5IDwtIGxpc3QoDQogICAgIk5vIFJ1biIgPSBkYXRhLmZyYW1lKCksDQogICAgIk5vIFN0YXRlIiA9IGRhdGEuZnJhbWUoKQ0KICApDQogIHJlc3VsdHNBYnVuZCA8LSBsaXN0KA0KICAgICJObyBSdW4iID0gIiIsDQogICAgIk5vIFN0YXRlIiA9ICIiDQogICkNCiAgc2VlZHMgPC0gdW5saXN0KHN0cnNwbGl0KHBhcmFtRnJhbWUkU2VlZFJ1bnNbaV0sICcsICcpKQ0KICBmb3IgKHNlZWQgaW4gc2VlZHMpIHsNCiAgICBmaWxlTmFtZSA8LSBmaWxlLnBhdGgoDQogICAgICBkaXJWaWtpbmdSZXN1bHRzW3BhcmFtRnJhbWUkRGF0YXNldElEW2ldXSwNCiAgICAgIHNwcmludGYocmVzdWx0Rm9ybWF0LCBwYXJhbUZyYW1lJENvbWJuTnVtW2ldLCBzZWVkKQ0KICAgICkNCiAgICANCiAgICBpZiAoZmlsZS5leGlzdHMoZmlsZU5hbWUpKSB7DQogICAgICB0ZW1wIDwtIGxvYWQoZmlsZU5hbWUpDQogICAgICB0ZW1wIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXApKSAjIEdldCBvYmplY3RzLg0KICAgICAgDQogICAgICBpZiAoaXMubGlzdCh0ZW1wKSAmJiAiUmVzdWx0IiAlaW4lIG5hbWVzKHRlbXApKSB7DQogICAgICAgIA0KICAgICAgICBpZiAoaXMuZGF0YS5mcmFtZSh0ZW1wJFJlc3VsdCkpDQogICAgICAgICAgY29tbXVuaXR5IDwtIHRlbXAkUmVzdWx0JENvbW11bml0eVtbbnJvdyh0ZW1wJFJlc3VsdCldXQ0KICAgICAgICBlbHNlIA0KICAgICAgICAgIGNvbW11bml0eSA8LSB0ZW1wJFJlc3VsdA0KICAgICAgICANCiAgICAgICAgc2l6ZSA8LSB0b1N0cmluZyhsZW5ndGgoY29tbXVuaXR5KSkNCiAgICAgICAgDQogICAgICAgIGlmIChjb21tdW5pdHlbMV0gIT0gIiIpIA0KICAgICAgICAgIGFidW5kIDwtIHRvU3RyaW5nKHRlbXAkQWJ1bmRbY29tbXVuaXR5ICsgMV0pDQogICAgICAgIGVsc2UgDQogICAgICAgICAgYWJ1bmQgPC0gIiINCiAgICAgICAgDQogICAgICAgIGNvbW11bml0eSA8LSB0b1N0cmluZyhjb21tdW5pdHkpDQogICAgICAgIA0KICAgICAgICBpZiAoY29tbXVuaXR5ID09ICIiKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCA8LSByZXN1bHRzTGlzdCRgTm8gU3RhdGVgICsgMQ0KICAgICAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSBpZiAoY29tbXVuaXR5ICVpbiUgbmFtZXMocmVzdWx0c0xpc3QpKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIHJlc3VsdHNTaXplW1tzaXplXV0gKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIDENCiAgICAgICAgICByZXN1bHRzQXNzZW1ibHlbW2NvbW11bml0eV1dIDwtIHRlbXANCiAgICAgICAgICByZXN1bHRzQWJ1bmRbW2NvbW11bml0eV1dIDwtIGFidW5kDQogICAgICAgICAgDQogICAgICAgICAgaWYgKHNpemUgJWluJSByZXN1bHRzU2l6ZSkgew0KICAgICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIDENCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgcmVzdWx0c1NpemUkYDBgIDwtIHJlc3VsdHNTaXplJGAwYCArIDENCiAgICAgIH0NCiAgICB9IGVsc2Ugew0KICAgICAgcmVzdWx0c0xpc3QkYE5vIFJ1bmAgPC0gcmVzdWx0c0xpc3QkYE5vIFJ1bmAgKyAxDQogICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgIH0NCiAgfQ0KICANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNbW2ldXSA8LSByZXN1bHRzTGlzdA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c0xpc3QpIC0gMiAjICEgTm8gU3RhdGUsIE5vIFJ1bg0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNbW2ldXSA8LSByZXN1bHRzU2l6ZQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNTaXplKSAtIDEgIyAhIDANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZUFzc2VtYmx5W1tpXV0gPC0gcmVzdWx0c0Fzc2VtYmx5DQogIHBhcmFtRnJhbWUkRW5kU3RhdGVBYnVuZGFuY2VbW2ldXSA8LSByZXN1bHRzQWJ1bmQNCn0NCmBgYA0KDQojIyBUZXN0IERhdGENCg0KYGBge3IgYWRkVGVzdH0NCnRlc3RSb3dOdW1zIDwtIG5yb3cocGFyYW1GcmFtZSkNCnRlc3RSb3dzVG9BZGQgPC0gYygyLCA2KSAjIE1ha2Ugc3VyZSB0byBwdXQgaW4gbnVtZXJpY2FsIG9yZGVyIQ0KDQpwYXJhbUZyYW1lIDwtIHdpdGgoDQogIGxpc3QoDQogICAgYmFzYWwyID0gYyg1LCAxMCwgMTUpLA0KICAgIGNvbnN1bWVyMiA9IGMoMjAsIDQwLCA2MCksDQogICAgbG9nQm9keVNpemUgPSBjKC0yLCAtMSwgLTEsIDApLA0KICAgIHBhcmFtZXRlcnMgPSBjKDAuMDEsIDEwLCAwLjUsIDAuMiwgMTAwLCAwLjEpDQogICksDQogIHsNCiAgICBzZXQuc2VlZCgzNjgwMTgwKQ0KICAgIHNlZWRzUHJlcDIgPC0gcnVuaWYoMiAqIGxlbmd0aChiYXNhbDIpICogbGVuZ3RoKGNvbnN1bWVyMikpICogMUU4DQogICAgd2l0aChsaXN0KA0KICAgICAgYiA9IHJlcChiYXNhbDIsIHRpbWVzID0gbGVuZ3RoKGNvbnN1bWVyMikpLA0KICAgICAgYyA9IHJlcChjb25zdW1lcjIsIGVhY2ggPSBsZW5ndGgoYmFzYWwyKSksDQogICAgICBzMSA9IHNlZWRzUHJlcDJbMToobGVuZ3RoKGJhc2FsMikgKiBsZW5ndGgoY29uc3VtZXIyKSldLA0KICAgICAgczIgPSBzZWVkc1ByZXAyWw0KICAgICAgICAobGVuZ3RoKGJhc2FsMikgKiBsZW5ndGgoY29uc3VtZXIyKSArIDEpOigNCiAgICAgICAgICAyICogbGVuZ3RoKGJhc2FsMikgKiBsZW5ndGgoY29uc3VtZXIyKSkNCiAgICAgIF0NCiAgICApLCB7DQogICAgICByYmluZCgNCiAgICAgICAgcGFyYW1GcmFtZSwNCiAgICAgICAgZGF0YS5mcmFtZSgNCiAgICAgICAgICBDb21ibk51bSA9IHRlc3RSb3dzVG9BZGQsDQogICAgICAgICAgQmFzYWxzID0gYlt0ZXN0Um93c1RvQWRkXSwNCiAgICAgICAgICBDb25zdW1lcnMgPSBjW3Rlc3RSb3dzVG9BZGRdLA0KICAgICAgICAgIFNlZWRQb29sID0gczFbdGVzdFJvd3NUb0FkZF0sDQogICAgICAgICAgU2VlZE1hdCA9IHMyW3Rlc3RSb3dzVG9BZGRdLA0KICAgICAgICAgIFNlZWRSdW5zID0gIiIsDQogICAgICAgICAgU2VlZFJ1bnNOdW0gPSAwLA0KICAgICAgICAgIEVuZFN0YXRlcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgodGVzdFJvd3NUb0FkZCkpKSwNCiAgICAgICAgICBFbmRTdGF0ZXNOdW0gPSAwLA0KICAgICAgICAgIEVuZFN0YXRlU2l6ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKHRlc3RSb3dzVG9BZGQpKSksDQogICAgICAgICAgRW5kU3RhdGVTaXplc051bSA9IE5BLA0KICAgICAgICAgIEVuZFN0YXRlQXNzZW1ibHkgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKHRlc3RSb3dzVG9BZGQpKSksDQogICAgICAgICAgRW5kU3RhdGVBYnVuZGFuY2UgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKHRlc3RSb3dzVG9BZGQpKSksDQogICAgICAgICAgRGF0YXNldCA9ICJUZXN0IiwNCiAgICAgICAgICBEYXRhc2V0SUQgPSBtYXgocGFyYW1GcmFtZSREYXRhc2V0SUQpICsgMSwNCiAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgICAgICAgKQ0KICAgICAgKQ0KICAgIH0NCiAgICApDQogIH0NCikNCg0KdGVzdFJvd051bXMgPC0gKHRlc3RSb3dOdW1zICsgMSk6bnJvdyhwYXJhbUZyYW1lKQ0KcmVzdWx0c0xpc3QgPC0gbGlzdCgNCiAgbGlzdCgNCiAgICAiTm8gUnVuIiA9IDAsDQogICAgIk5vIFN0YXRlIiA9IDAsDQogICAgIjIsIDQsIDYsIDEyLCAyOSIgPSAxLA0KICAgICIyLCA0LCA2LCAxMywgMjkiID0gMQ0KICApLA0KICBsaXN0KA0KICAgICJObyBSdW4iID0gMCwNCiAgICAiTm8gU3RhdGUiID0gMCwNCiAgICAiOCwgMTAsIDEyLCAxNCwgMTUsIDE2LCAzOSwgNDMiID0gMSwNCiAgICAiOCwgMTIsIDE0LCAxNSwgMTYsIDM4LCAzOSIgPSAxDQogICkNCikNCnJlc3VsdHNTaXplIDwtIGxpc3QoDQogIGxpc3QoDQogICAgIjAiID0gMCwNCiAgICAiNSIgPSAyDQogICksDQogIGxpc3QoDQogICAgIjAiID0gMCwNCiAgICAiOCIgPSAxLA0KICAgICI3IiA9IDENCiAgKQ0KKQ0KcmVzdWx0c0FidW5kIDwtIGxpc3QoDQogIGxpc3QoDQogICAgIk5vIFJ1biIgPSAiIiwNCiAgICAiTm8gU3RhdGUiID0gIiIsDQogICAgIjIsIDQsIDYsIDEyLCAyOSIgPSAiNzQyLjg4NTUzNjcxNzEyLCA4MC41NzkyMzMwNzI2MjYsIDE2Mi4xMjgzOTk4NTAyNTMsIDIwLjIwODIxOTg2OTkzODksIDE4Ljg1ODk0OTA1MTA0MjkiLA0KICAgICIyLCA0LCA2LCAxMywgMjkiID0gIjY2OC42NjQxNDM1ODE4MzcsIDExOS4wMjQxNDY4NTEwNTIsIDEyNy42ODAyNjkzODM4NjcsIDMwLjY1Nzk2MDg2NjAzMywgMTMuNDg0NDE5NDcwNzk0NCINCiAgKSwNCiAgbGlzdCgNCiAgICAiTm8gUnVuIiA9ICIiLA0KICAgICJObyBTdGF0ZSIgPSAiIiwNCiAgICAiOCwgMTAsIDEyLCAxNCwgMTUsIDE2LCAzOSwgNDMiID0gIjIwLjc2NjU4MDc2MDY0OTEsIDMyLjQ0NjExNjUyNjE0NTQsIDgwLjQwMzMzODc4MTg4OTUsIDgxNy44Nzk3MjIwMzMzNTQsIDEyMS4xMzY1NzA3ODI4MjgsIDE4LjAzOTA2NzEwODg5NTcsIDEyLjM4MzQ1NjExNzcyNzEsIDE5Ljk2NzQ3MTg1NDMxOTYiLA0KICAgICI4LCAxMiwgMTQsIDE1LCAxNiwgMzgsIDM5IiA9ICI4Mi41OTIwNDg0OTI4MTIsIDEzOC4yNjczNzkxNjYwMTQsIDkzOC4xNTg0MzYzNzkxNjYsIDUxLjg2MTA5NjM3NDUwMjEsIDUuMDM1NTYyNTE4Mzc0OTEsIDE0LjEwMTkzNDMxNDU4MjUsIDI1LjkyMzEwNjI3MTEyMjgiDQogICkNCikNCg0KZm9yIChqIGluIHNlcV9hbG9uZyh0ZXN0Um93TnVtcykpIHsNCiAgaSA8LSB0ZXN0Um93TnVtc1tqXQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc1tbaV1dIDwtIHJlc3VsdHNMaXN0W1tqXV0NCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNMaXN0W1tqXV0pIC0gMiAjICEgTm8gU3RhdGUsIE5vIFJ1bg0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNbW2ldXSA8LSByZXN1bHRzU2l6ZVtbal1dDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVTaXplc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c1NpemVbW2pdXSkgLSAxICMgISAwDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVBYnVuZGFuY2VbW2ldXSA8LSByZXN1bHRzQWJ1bmRbW2pdXQ0KfQ0KDQpgYGANCg0KIyMgUGxvdA0KDQpgYGB7ciBwbG90M0R9DQojIFgsIFksIEJhc2FsIGFuZCBDb25zdW1lci4NCiMgWiA9IFNpemVzIG9mIHRoZSBFbmRzdGF0ZXMuDQoNCnBsb3RTY2FsaW5nRGF0YSA8LSBkYXRhLmZyYW1lKA0KICBDb21ibk51bSA9IHJlcChwYXJhbUZyYW1lJENvbWJuTnVtLCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSksDQogIEJhc2FscyA9IHJlcChwYXJhbUZyYW1lJEJhc2FscywgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW0pLA0KICBDb25zdW1lcnMgPSByZXAocGFyYW1GcmFtZSRDb25zdW1lcnMsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgRGF0YXNldCA9IHJlcChwYXJhbUZyYW1lJERhdGFzZXQsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgRGF0YXNldElEID0gcmVwKHBhcmFtRnJhbWUkRGF0YXNldElELCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSkNCikNCg0KIyBDb21tdW5pdGllcw0KY29tbXMgPC0gdW5saXN0KGxhcHBseShwYXJhbUZyYW1lJEVuZFN0YXRlcywgbmFtZXMpKQ0KZnJlcXMgPC0gdW5saXN0KHBhcmFtRnJhbWUkRW5kU3RhdGVzKQ0KYXNtYmwgPC0gdW5saXN0KHBhcmFtRnJhbWUkRW5kU3RhdGVBc3NlbWJseSwgcmVjdXJzaXZlID0gRkFMU0UpDQphc21ibCA8LSBhc21ibFtjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQpmcmVxcyA8LSBmcmVxc1tjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQpjb21tcyA8LSBjb21tc1tjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQoNCmFzbWJsIDwtIGxhcHBseShhc21ibCwgZnVuY3Rpb24oZCkgew0KICBpZiAoaXMubnVsbChkKSkgcmV0dXJuKE5BKQ0KICBpZiAoIlJlc3VsdC5PdXRjb21lIiAlaW4lIG5hbWVzKGQpKQ0KICAgIGQgJT4lIGRwbHlyOjpmaWx0ZXIoUmVzdWx0Lk91dGNvbWUgIT0gIlR5cGUgMSAoRmFpbHVyZSkiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgIFJlc3VsdC5PdXRjb21lICE9ICJQcmVzZW50IikNCiAgZWxzZQ0KICAgIGQkUmVzdWx0ICU+JSBkcGx5cjo6ZmlsdGVyKE91dGNvbWUgIT0gIlR5cGUgMSAoRmFpbHVyZSkiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPdXRjb21lICE9ICJQcmVzZW50IikNCn0pDQoNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcyA8LSBjb21tcw0KcGxvdFNjYWxpbmdEYXRhJENvbW11bml0eUZyZXEgPC0gZnJlcXMNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTZXEgPC0gYXNtYmwNCg0KIyBDb21tdW5pdHkgU2l6ZQ0KdGVtcCA8LSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KHBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcywgJywnKSwgbGVuZ3RoKSkNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTaXplIDwtIHRlbXANCg0KIyBGb3IgdXNhZ2UgYnkgdGhlIHJlYWRlci4NCg0KcGxvdFNjYWxpbmcgPC0gcGxvdGx5OjpwbG90X2x5KA0KICBwbG90U2NhbGluZ0RhdGEsDQogIHggPSB+QmFzYWxzLA0KICB5ID0gfkNvbnN1bWVycywNCiAgeiA9IH5Db21tdW5pdHlTaXplLA0KICBjb2xvciA9IH5EYXRhc2V0LA0KICBjb2xvcnMgPSBjKCJyZWQiLCAiYmx1ZSIsICJibGFjayIpDQopDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6YWRkX21hcmtlcnMocGxvdFNjYWxpbmcpDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6bGF5b3V0KA0KICBwbG90U2NhbGluZywNCiAgc2NlbmUgPSBsaXN0KA0KICAgIHhheGlzID0gbGlzdCh0eXBlID0gImxvZyIpLA0KICAgIHlheGlzID0gbGlzdCh0eXBlID0gImxvZyIpLA0KICAgIGNhbWVyYSA9IGxpc3QoDQogICAgICBleWUgPSBsaXN0KA0KICAgICAgICB4ID0gLTEuMjUsIHkgPSAtMS4yNSwgeiA9IC4wNQ0KICAgICAgKQ0KICAgICkNCiAgKQ0KKQ0KDQpwbG90U2NhbGluZw0KYGBgDQoNCiMjIEFidW5kYW5jZXMNCg0KYGBge3IgbG9hZFBvb2xzTWF0c30NCiMgPiBydW5pZigxKSAqIDFFOA0KIyBbMV0gODI1OTg2NzkNCnNldC5zZWVkKDgyNTk4Njc5KQ0KDQptYXRzIDwtIGxpc3QoKQ0KcG9vbHNhbGwgPC0gbGlzdCgpICMgbmFtZSBwb29scyB1c2VkIGluIHNhdmUgZGF0YTsgYmUgY2FyZWZ1bCENCg0KZm9yIChpIGluIDE6bGVuZ3RoKGRpclZpa2luZykpIHsNCiAgdGVtcCA8LSBsb2FkKGZpbGUucGF0aCgNCiAgICBkaXJWaWtpbmdbaV0sIA0KICAgIHBhc3RlMCgiTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZy1Qb29sTWF0cyIsIA0KICAgICAgICAgICBpZiAoaSA+IDEpIGkgZWxzZSAiIiwgDQogICAgICAgICAgICIuUkRTIikNCiAgKSkNCiAgbWF0c1tbaV1dIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXBbMV0pKQ0KICBwb29sc2FsbFtbaV1dIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXBbMl0pKQ0KfQ0KcG9vbHMgPC0gcG9vbHNhbGwNCg0KIyBBZGQgaW4gdGhlIHRlc3QgZGF0YXNldHMuDQpwb29sc1RlbXAgPC0gbGlzdCgpDQptYXRzVGVtcCA8LSBsaXN0KCkNCmZvciAociBpbiB0ZXN0Um93TnVtcykgew0KICB0ZXN0Um93Um93IDwtIHBhcmFtRnJhbWVbciwgXQ0KICBwb29sc1RlbXBbW3Rlc3RSb3dSb3ckQ29tYm5OdW1dXSA8LSB3aXRoKHRlc3RSb3dSb3csDQogICAgUk1UUkNvZGUyOjpMYXdNb3J0b24xOTk2X3NwZWNpZXMoDQogICAgICBCYXNhbCA9IEJhc2FscywNCiAgICAgIENvbnN1bWVyID0gQ29uc3VtZXJzLA0KICAgICAgUGFyYW1ldGVycyA9IGMoMC4wMSwgMTAsIDAuNSwgMC4yLCAxMDAsIDAuMSksDQogICAgICBMb2dCb2R5U2l6ZSA9IGMoLTIsIC0xLCAtMSwgMCksDQogICAgICBzZWVkID0gU2VlZFBvb2wNCiAgICApDQogICkNCiAgbWF0c1RlbXBbW3Rlc3RSb3dSb3ckQ29tYm5OdW1dXSA8LSB3aXRoKHRlc3RSb3dSb3csDQogICAgUk1UUkNvZGUyOjpMYXdNb3J0b24xOTk2X0NvbW11bml0eU1hdCgNCiAgICAgIFBvb2wgPSBwb29sc1RlbXBbW0NvbWJuTnVtXV0sDQogICAgICBQYXJhbWV0ZXJzID0gYygwLjAxLCAxMCwgMC41LCAwLjIsIDEwMCwgMC4xKSwNCiAgICAgIHNlZWQgPSBTZWVkTWF0DQogICAgKQ0KICApDQp9DQpwb29sc1tbaSArIDFdXSA8LSBwb29sc1RlbXANCm1hdHNbW2kgKyAxXV0gPC0gbWF0c1RlbXANCg0Kb2xkQ2FuZGlkYXRlRGF0YSA8LSBsb2FkKGZpbGUucGF0aChnZXR3ZCgpLCAiY2FuZGlkYXRlRGF0YVNvRmFyLlJkYXRhIikpDQpvbGRDYW5kaWRhdGVEYXRhIDwtIGV2YWwocGFyc2UodGV4dCA9IG9sZENhbmRpZGF0ZURhdGEpKQ0KYGBgDQoNCmBgYHtyIGNvbXB1dGVDYW5kaWRhdGVzfQ0KY2FuZGlkYXRlRGF0YSA8LSBwbG90U2NhbGluZ0RhdGEgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgQ29tYm5OdW0sIERhdGFzZXQNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIE90aGVyU3RlYWR5U3RhdGVzID0gZHBseXI6Om4oKSAtIDENCikgJT4lIGRwbHlyOjpmaWx0ZXIoDQogIE90aGVyU3RlYWR5U3RhdGVzID4gMA0KKQ0KY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OnNlbGVjdCgtQ29tbXVuaXR5U2VxKQ0KYGBgDQoNCmBgYHtyIGxvYWRBYnVuZGFuY2VzfQ0KIyBGaXJzdCwgY2hlY2sgaWYgaXQgaXMgaW4gdGhlIHBhcmFtRnJhbWUuDQojIFNlY29uZCwgY2hlY2sgaWYgaXQgaXMgaW4gdGhlIHNhdmVkIGRhdGEgZnJvbSB0aGUgcHJldmlvdXMuDQojIE90aGVyd2lzZSwgaWdub3JlIGl0LCB3ZSdsbCBmaWd1cmUgb3V0IHdoYXQgaXQgaXMgYW5kIHdoeSBpdCBpcyBtaXNzaW5nIGxhdGVyLg0KDQpjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kIDwtICIiDQoNCmZvciAociBpbiAxOm5yb3coY2FuZGlkYXRlRGF0YSkpIHsNCiAgIyBJRCAxOjQgYXJlIHVzZWQgdG8gaWRlbnRpZnkgcGFyYW1GcmFtZSwgNSB1c2VkIHRvIGlkZW50aWZ5IGFidW5kYW5jZQ0KICBJRCA8LSBjYW5kaWRhdGVEYXRhW3IsIDE6Nl0NCiAgcGFyYW1GcmFtZVJvdyA8LSBwYXJhbUZyYW1lICU+JSBkcGx5cjo6ZmlsdGVyKA0KICAgIENvbWJuTnVtID09IElEJENvbWJuTnVtLA0KICAgIEJhc2FscyA9PSBJRCRCYXNhbHMsDQogICAgQ29uc3VtZXJzID09IElEJENvbnN1bWVycywNCiAgICBEYXRhc2V0ID09IElEJERhdGFzZXQNCiAgKQ0KICANCiAgaWYgKGlzLmxpc3QocGFyYW1GcmFtZVJvdyRFbmRTdGF0ZUFidW5kYW5jZVtbMV1dKSkgew0KICAgIGVudHJ5IDwtIHdoaWNoKElEJENvbW11bml0aWVzID09IG5hbWVzKHBhcmFtRnJhbWVSb3ckRW5kU3RhdGVBYnVuZGFuY2VbWzFdXSkpDQogICAgaWYgKGxlbmd0aChlbnRyeSkpIHsNCiAgICAgIGNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5QWJ1bmRbcl0gPC0gcGFyYW1GcmFtZVJvdyRFbmRTdGF0ZUFidW5kYW5jZVtbMV1dW1tlbnRyeV1dDQogICAgICBuZXh0KCkNCiAgICB9DQogIH0NCiAgDQogIGlmIChJRCREYXRhc2V0ID09ICIyMDIxLTA0Iikgew0KICAgIA0KICAgIG9sZENhbmREYXRSb3cgPC0gb2xkQ2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcigNCiAgICAgIENvbWJuTnVtID09IElEJENvbWJuTnVtLA0KICAgICAgQmFzYWxzID09IElEJEJhc2FscywNCiAgICAgIENvbnN1bWVycyA9PSBJRCRDb25zdW1lcnMsDQogICAgICBDb21tdW5pdGllcyA9PSBJRCRDb21tdW5pdGllcw0KICAgICkNCiAgICANCiAgICBpZiAobnJvdyhvbGRDYW5kRGF0Um93KSA+IDApIHsNCiAgICAgIGlmIChvbGRDYW5kRGF0Um93JENvbW11bml0eUFidW5kICE9ICIiKSB7DQogICAgICAgIGNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5QWJ1bmRbcl0gPC0gb2xkQ2FuZERhdFJvdyRDb21tdW5pdHlBYnVuZA0KICAgICAgfQ0KICAgIH0NCiAgfQ0KfQ0KYGBgDQoNCmBgYHtyIGNyZWF0ZUFidW5kfQ0KZm9yIChyIGluIDE6bnJvdyhjYW5kaWRhdGVEYXRhKSkgew0KICBpZiAoIShjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kW3JdID09ICJGYWlsdXJlIiB8DQogICAgICBjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kW3JdID09ICIiKSkgbmV4dA0KDQogICMgUmFuZG9tIGd1ZXNzZXMsIHN0YXJ0aW5nIGZyb20gc3RydWN0dXJlZC4NCiAgdGVtcCA8LSB3aXRoKA0KICAgIGNhbmRpZGF0ZURhdGFbciwgXSwNCiAgICBSTVRSQ29kZTI6OkZpbmRTdGVhZHlTdGF0ZUZyb21Fc3RpbWF0ZSgNCiAgICAgIFBvb2wgPSBwb29sc1tbRGF0YXNldElEXV1bW0NvbWJuTnVtXV0sDQogICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dLA0KICAgICAgQ29tbXVuaXR5ID0gQ29tbXVuaXRpZXMsDQogICAgICBQb3B1bGF0aW9ucyA9IGlmZWxzZSgNCiAgICAgICAgcG9vbHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dJFR5cGVbDQogICAgICAgICAgUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdChDb21tdW5pdGllcykNCiAgICAgICAgXSA9PSAiQmFzYWwiLA0KICAgICAgICAxMDAwLCAxMCkNCiAgICApDQogICkNCg0KICBpZiAoYW55KHRlbXApIDwgMUUtNCkgew0KICAgIHRlbXAgPC0gIkVzdGltYXRlRmFpbHVyZSINCiAgfSBlbHNlIHsNCiAgICB0ZW1wIDwtIHRvU3RyaW5nKHRlbXApDQogIH0NCiAgY2FuZGlkYXRlRGF0YSRDb21tdW5pdHlBYnVuZFtyXSA8LSB0ZW1wDQp9DQpgYGANCg0KYGBge3IgZmlsdGVyTm9BYnVuZH0NCmNhbmRpZGF0ZURhdGEgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihDb21tdW5pdHlBYnVuZCAhPSAiIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb21tdW5pdHlBYnVuZCAhPSAiRmFpbHVyZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29tbXVuaXR5QWJ1bmQgIT0gIkVzdGltYXRlRmFpbHVyZSIpDQpgYGANCg0KYGBge3IgY29tcHV0ZVByb2R1Y3Rpdml0eX0NCmNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5UHJvZCA8LSBOQQ0KZm9yIChyIGluIDE6bnJvdyhjYW5kaWRhdGVEYXRhKSkgew0KICBjYW5kaWRhdGVEYXRhJENvbW11bml0eVByb2Rbcl0gPC0gd2l0aCgNCiAgICBjYW5kaWRhdGVEYXRhW3IsIF0sIA0KICAgIFJNVFJDb2RlMjo6UHJvZHVjdGl2aXR5KA0KICAgICAgUG9vbCA9IHBvb2xzW1tEYXRhc2V0SURdXVtbQ29tYm5OdW1dXSwgDQogICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dLCANCiAgICAgIENvbW11bml0eSA9IENvbW11bml0aWVzLCANCiAgICAgIFBvcHVsYXRpb25zID0gQ29tbXVuaXR5QWJ1bmQNCiAgICApDQogICkNCn0NCmBgYA0KDQojIyBTaW1wbGUgSXNsYW5kIFJlc3VsdHMNCmBgYHtyIGlzbGFuZEZVTn0NCmlzbGFuZEZVTiA8LSBmdW5jdGlvbihpLCBkYXQsIHBvb2wsIG1hdCwgZG1hdCkgew0KICB0ZW1wIDwtIGRhdFtpLCBdDQogIFJNVFJDb2RlMjo6SXNsYW5kRHluYW1pY3MoDQogICAgUG9vbCA9IHBvb2wsDQogICAgSW50ZXJhY3Rpb25NYXRyaXggPSBtYXQsDQogICAgQ29tbXVuaXRpZXMgPSBjKA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0aWVzWzFdKSwNCiAgICAgIHJlcCgiIiwgbnJvdyhkbWF0KSAtIDIpLA0KICAgICAgdGVtcCRDb21tdW5pdGllc1syXQ0KICAgICksDQogICAgUG9wdWxhdGlvbnMgPSBjKA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0eUFidW5kWzFdKSwNCiAgICAgIHJlcCgiIiwgbnJvdyhkbWF0KSAtIDIpLA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0eUFidW5kWzJdKQ0KICAgICksDQogICAgRGlzcGVyc2FsUG9vbCA9IDAuMDAwMSwNCiAgICBEaXNwZXJzYWxJc2xhbmQgPSBkbWF0LA0KICAgIFZlcmJvc2UgPSBGQUxTRQ0KICApDQp9DQpgYGANCg0KYGBge3IgaXNsYW5kT25lVHdvfQ0KIyBGb3IgZWFjaCBncm91cC1kYXRhc2V0LA0KIyBGb3IgZWFjaCBwYWlyLA0KIyBSdW4gSXNsYW5kIER5bmFtaWNzLA0KIyBTYXZlIHRoZSByZXN1bHQgd2l0aCBpdHMgcGFpcmluZw0KY2FuZGlkYXRlRGF0YSRUb3RhbElEIDwtIHBhc3RlKGNhbmRpZGF0ZURhdGEkQ29tYm5OdW0sIGNhbmRpZGF0ZURhdGEkRGF0YXNldElEKQ0KDQppc2xhbmRJbnRlcmFjdGlvbnNPbmVUd28gPC0gbGlzdCgpDQoNCmZvciAoZ3JwIGluIHVuaXF1ZShjYW5kaWRhdGVEYXRhJFRvdGFsSUQpKSB7DQogIGNhbmRpZGF0ZURhdGFTdWJzZXQgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihUb3RhbElEID09IGdycCkNCiAgDQogIGlmIChucm93KGNhbmRpZGF0ZURhdGFTdWJzZXQpID09IDEpIG5leHQoKQ0KICANCiAgcGFpcmluZ1Jlc3VsdHMgPC0gY29tYm4oDQogICAgbnJvdyhjYW5kaWRhdGVEYXRhU3Vic2V0KSwgMiwgDQogICAgaXNsYW5kRlVOLA0KICAgIGRhdCA9IGNhbmRpZGF0ZURhdGFTdWJzZXQsIA0KICAgIHBvb2wgPSBwb29sc1tbDQogICAgICBjYW5kaWRhdGVEYXRhU3Vic2V0JERhdGFzZXRJRFsxXQ0KICAgIF1dW1tjYW5kaWRhdGVEYXRhU3Vic2V0JENvbWJuTnVtWzFdXV0sDQogICAgbWF0ID0gbWF0c1tbDQogICAgICBjYW5kaWRhdGVEYXRhU3Vic2V0JERhdGFzZXRJRFsxXQ0KICAgIF1dW1tjYW5kaWRhdGVEYXRhU3Vic2V0JENvbWJuTnVtWzFdXV0sDQogICAgZG1hdCA9IG1hdHJpeChjKDAsIDEsIDEsIDApLCBucm93ID0gMiwgbmNvbCA9IDIpLA0KICAgIHNpbXBsaWZ5ID0gRkFMU0UNCiAgKQ0KICANCiAgcGFpcmluZ1Jlc3VsdHMgPC0gbGFwcGx5KA0KICAgIHBhaXJpbmdSZXN1bHRzLCBmdW5jdGlvbihtYXQsIGlzbGVzKSB7DQogICAgICBtYXQgPC0gbWF0W25yb3cobWF0KSwgLTFdDQogICAgICByZXRWYWwgPC0gbGlzdCgpDQogICAgICBzcGVjaWVzIDwtIGxlbmd0aChtYXQpIC8gaXNsZXMNCiAgICAgIGZvciAoaSBpbiAxOmlzbGVzKSB7DQogICAgICAgIHJldFZhbFtbaV1dIDwtIG1hdFsoKGkgLSAxKSAqIHNwZWNpZXMgKyAxKSA6IChpICogc3BlY2llcyldDQogICAgICB9DQogICAgICByZXRWYWwNCiAgICB9LA0KICAgIGlzbGVzID0gMg0KICApDQogIA0KICBpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29bW2dycF1dIDwtIHBhaXJpbmdSZXN1bHRzDQp9DQpgYGANCg0KYGBge3IgaXNsYW5kT25lRW1wdHlUd299DQppc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3byA8LSBsaXN0KCkNCg0KZm9yIChncnAgaW4gdW5pcXVlKGNhbmRpZGF0ZURhdGEkVG90YWxJRCkpIHsNCiAgY2FuZGlkYXRlRGF0YVN1YnNldCA8LSBjYW5kaWRhdGVEYXRhICU+JSBkcGx5cjo6ZmlsdGVyKFRvdGFsSUQgPT0gZ3JwKQ0KICANCiAgaWYgKG5yb3coY2FuZGlkYXRlRGF0YVN1YnNldCkgPT0gMSkgbmV4dCgpDQogIA0KICBwYWlyaW5nUmVzdWx0cyA8LSBjb21ibigNCiAgICBucm93KGNhbmRpZGF0ZURhdGFTdWJzZXQpLCAyLCANCiAgICBpc2xhbmRGVU4sDQogICAgZGF0ID0gY2FuZGlkYXRlRGF0YVN1YnNldCwgDQogICAgcG9vbCA9IHBvb2xzW1sNCiAgICAgIGNhbmRpZGF0ZURhdGFTdWJzZXQkRGF0YXNldElEWzFdDQogICAgXV1bW2NhbmRpZGF0ZURhdGFTdWJzZXQkQ29tYm5OdW1bMV1dXSwNCiAgICBtYXQgPSBtYXRzW1sNCiAgICAgIGNhbmRpZGF0ZURhdGFTdWJzZXQkRGF0YXNldElEWzFdDQogICAgXV1bW2NhbmRpZGF0ZURhdGFTdWJzZXQkQ29tYm5OdW1bMV1dXSwNCiAgICBkbWF0ID0gbWF0cml4KGMoDQogICAgICAwLCAxLCAwLCAjIElzbGFuZCAyIC0+IDENCiAgICAgIDEsIDAsIDEsICMgSXNsYW5kIDEgLT4gMiwgSXNsYW5kIDMgLT4gMg0KICAgICAgMCwgMSwgMCAgIyBJc2xhbmQgMiAtPiAzDQogICAgKSwgbnJvdyA9IDMsIG5jb2wgPSAzLCBieXJvdyA9IFRSVUUpLA0KICAgIHNpbXBsaWZ5ID0gRkFMU0UNCiAgKQ0KICANCiAgcGFpcmluZ1Jlc3VsdHMgPC0gbGFwcGx5KA0KICAgIHBhaXJpbmdSZXN1bHRzLCBmdW5jdGlvbihtYXQsIGlzbGVzKSB7DQogICAgICBtYXQgPC0gbWF0W25yb3cobWF0KSwgLTFdDQogICAgICByZXRWYWwgPC0gbGlzdCgpDQogICAgICBzcGVjaWVzIDwtIGxlbmd0aChtYXQpIC8gaXNsZXMNCiAgICAgIGZvciAoaSBpbiAxOmlzbGVzKSB7DQogICAgICAgIHJldFZhbFtbaV1dIDwtIG1hdFsoKGkgLSAxKSAqIHNwZWNpZXMgKyAxKSA6IChpICogc3BlY2llcyldDQogICAgICB9DQogICAgICByZXRWYWwNCiAgICB9LA0KICAgIGlzbGVzID0gMw0KICApDQogIA0KICBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1tbZ3JwXV0gPC0gcGFpcmluZ1Jlc3VsdHMNCn0NCmBgYA0KDQpgYGB7ciBjb21wYXJlSXNsYW5kRHluYW1pY3N9DQojIEZvcm1hdCBvZiB0YWJsZSBzaG91bGQgYmU6DQojIElELCBDb21tdW5pdHkgMSwgQ29tbXVuaXR5IDIsIE91dGNvbWVzIDEtMiwgT3V0Y29tZXMgMS0wLTINCiMgRm9yIG91dGNvbWVzLCBzcGVjaWVzIHByZXNlbmNlIHdpbGwgYmUgdXNlZC4NCg0KIyBjb21tdW5pdGllcyA8LSBOVUxMDQojIHRvdGFsQ29tbXVuaXRpZXMgPC0gTlVMTA0KIyBmb3IgKGdycCBpbiB1bmlxdWUoY2FuZGlkYXRlRGF0YSRUb3RhbElEKSkgew0KIyAgIGNhbmRpZGF0ZURhdGFTdWJzZXQgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihUb3RhbElEID09IGdycCkNCiMgICANCiMgICBpZiAobnJvdyhjYW5kaWRhdGVEYXRhU3Vic2V0KSA+IDEpIHsNCiMgICAgIG5ld0NvbW11bml0aWVzIDwtIGNvbWJuKA0KIyAgICAgICBjYW5kaWRhdGVEYXRhU3Vic2V0JENvbW11bml0aWVzLCAyLCANCiMgICAgICkNCiMgICAgIGNvbW11bml0aWVzIDwtIGMoY29tbXVuaXRpZXMsIG5ld0NvbW11bml0aWVzKQ0KIyAgICAgdG90YWxDb21tdW5pdGllcyA8LSBjKA0KIyAgICAgICB0b3RhbENvbW11bml0aWVzLA0KIyAgICAgICB0b1N0cmluZyhzb3J0KHVuaXF1ZSh1bmxpc3QobGFwcGx5KG5ld0NvbW11bml0aWVzLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSTVRSQ29kZTI6OkNzdlJvd1NwbGl0KSkpKSkNCiMgICAgICkNCiMgICB9DQojIH0NCg0KY29tbXVuaXRpZXMgPC0gTlVMTA0KdG90YWxDb21tdW5pdGllcyA8LSBOVUxMDQpmb3IgKGdycCBpbiB1bmlxdWUoY2FuZGlkYXRlRGF0YSRUb3RhbElEKSkgew0KICBjYW5kaWRhdGVEYXRhU3Vic2V0IDwtIGNhbmRpZGF0ZURhdGEgJT4lIGRwbHlyOjpmaWx0ZXIoVG90YWxJRCA9PSBncnApDQogIA0KICBpZiAobnJvdyhjYW5kaWRhdGVEYXRhU3Vic2V0KSA+IDEpIHsNCiAgICBuZXdDb21tdW5pdGllcyA8LSBjb21ibigNCiAgICAgIGNhbmRpZGF0ZURhdGFTdWJzZXQkQ29tbXVuaXRpZXMsIDIsIA0KICAgICkNCiAgICBjb21tdW5pdGllcyA8LSBjKGNvbW11bml0aWVzLCBuZXdDb21tdW5pdGllcykNCiAgICB0b3RhbENvbW11bml0aWVzIDwtIGMoICMgTGFiZWxsaW5nIGlzIHdyb25nIGhlcmUuIE5lZWQgdEMgZnJvbSBwYWlycyBvZiBuQy4NCiAgICAgIHRvdGFsQ29tbXVuaXRpZXMsDQogICAgICBsaXN0KGFwcGx5KG5ld0NvbW11bml0aWVzLCAyLCBmdW5jdGlvbihjb21zKSB7DQogICAgICAgIHRvU3RyaW5nKHNvcnQodW5pcXVlKFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQoY29tcykpKSkNCiAgICAgIH0pKQ0KICAgICkNCiAgfQ0KfQ0KDQptaW5UaHJlc2ggPC0gMUUtNg0KaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2ggPC0gdW5saXN0KGxhcHBseSgNCiAgICBzZXFfYWxvbmcoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvKSwgZnVuY3Rpb24oaSwgYnlJRCwgY29tbXVuaXRpZXMpIHsNCiAgICAgICAgbGFwcGx5KHNlcV9hbG9uZyhieUlEW1tpXV0pLCBmdW5jdGlvbihqLCBzeXN0ZW0sIGNvbW1zKSB7DQogICAgICAgICAgICBsYXBwbHkoc3lzdGVtW1tqXV0sIGZ1bmN0aW9uKHBpcGVFbmQsIGNvbW1zKSB7DQogICAgICAgICAgICAgICAgdG9TdHJpbmcoUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdChjb21tcylbDQogICAgICAgICAgICAgICAgICAgIHdoaWNoKHBpcGVFbmQgPiBtaW5UaHJlc2gpDQogICAgICAgICAgICAgICAgXSkNCiAgICAgICAgICAgIH0sIGNvbW1zID0gY29tbXNbW2pdXSkNCiAgICAgICAgfSwgc3lzdGVtID0gYnlJRFtbaV1dLCBjb21tcyA9IGNvbW11bml0aWVzW1tpXV0pDQogICAgfSwgYnlJRCA9IGlzbGFuZEludGVyYWN0aW9uc09uZVR3bywgY29tbXVuaXRpZXMgPSB0b3RhbENvbW11bml0aWVzDQopKQ0KDQppc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoIDwtIHVubGlzdChsYXBwbHkoDQogICAgc2VxX2Fsb25nKGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvKSwgZnVuY3Rpb24oaSwgYnlJRCwgY29tbXVuaXRpZXMpIHsNCiAgICAgICAgbGFwcGx5KHNlcV9hbG9uZyhieUlEW1tpXV0pLCBmdW5jdGlvbihqLCBzeXN0ZW0sIGNvbW1zKSB7DQogICAgICAgICAgICBsYXBwbHkoc3lzdGVtW1tqXV0sIGZ1bmN0aW9uKHBpcGVFbmQsIGNvbW1zKSB7DQogICAgICAgICAgICAgICAgdG9TdHJpbmcoUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdChjb21tcylbDQogICAgICAgICAgICAgICAgICAgIHdoaWNoKHBpcGVFbmQgPiBtaW5UaHJlc2gpDQogICAgICAgICAgICAgICAgXSkNCiAgICAgICAgICAgIH0sIGNvbW1zID0gY29tbXNbW2pdXSkNCiAgICAgICAgfSwgc3lzdGVtID0gYnlJRFtbaV1dLCBjb21tcyA9IGNvbW11bml0aWVzW1tpXV0pDQogICAgfSwgYnlJRCA9IGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvLCBjb21tdW5pdGllcyA9IHRvdGFsQ29tbXVuaXRpZXMNCikpDQoNCiMgaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2ggPC0gdW5saXN0KGxhcHBseSgNCiMgICBzZXFfYWxvbmcoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvKSwgZnVuY3Rpb24oaSwgeCwgdEMpIHsNCiMgICAgIGxhcHBseSh4W1tpXV0sIGZ1bmN0aW9uKHksIHRDKSB7DQojICAgICAgIGxhcHBseSh5LCBmdW5jdGlvbih6LCB0Qykgew0KIyAgICAgICAgIHRvU3RyaW5nKFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQodEMpW3doaWNoKHogPiAxRS02KV0pDQojICAgICAgIH0sIHRDID0gdEMpDQojICAgICB9LA0KIyAgICAgdEMgPSB0Q1tpXSkNCiMgICB9LA0KIyAgIHggPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd28sDQojICAgdEMgPSB0b3RhbENvbW11bml0aWVzKSkNCiMgDQojIGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvV2hpY2ggPC0gdW5saXN0KGxhcHBseSgNCiMgICBzZXFfYWxvbmcoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd28pLCBmdW5jdGlvbihpLCB4LCB0Qykgew0KIyAgICAgbGFwcGx5KHhbW2ldXSwgZnVuY3Rpb24oeSwgdEMpIHsNCiMgICAgICAgbGFwcGx5KHksIGZ1bmN0aW9uKHosIHRDKSB7DQojICAgICAgICAgdG9TdHJpbmcoUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdCh0Qylbd2hpY2goeiA+IDFFLTYpXSkNCiMgICAgICAgfSwgdEMgPSB0QykNCiMgICAgIH0sDQojICAgICB0QyA9IHRDW2ldKQ0KIyAgIH0sDQojICAgeCA9IGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvLA0KIyAgIHRDID0gdG90YWxDb21tdW5pdGllcykpDQoNCmlzbGFuZEludGVyYWN0aW9uUmVzdWx0cyA8LSBkYXRhLmZyYW1lKA0KICBEYXRhc2V0SUQgPSByZXAobmFtZXMoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvKSwgDQogICAgICAgICAgICAgICAgICB1bmxpc3QobGFwcGx5KGlzbGFuZEludGVyYWN0aW9uc09uZVR3bywgbGVuZ3RoKSkpLA0KICBDb21tdW5pdHkxID0gY29tbXVuaXRpZXNbc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChjb21tdW5pdGllcyksIGJ5ID0gMildLA0KICBDb21tdW5pdHkyID0gY29tbXVuaXRpZXNbc2VxKGZyb20gPSAyLCB0byA9IGxlbmd0aChjb21tdW5pdGllcyksIGJ5ID0gMildLA0KICBPdXRjb21lV09FbXB0eV9Jc2xhbmQxID0gaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2hbDQogICAgc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaCksIGJ5ID0gMildLA0KICBPdXRjb21lV09FbXB0eV9Jc2xhbmQyID0gaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2hbDQogICAgc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaCksIGJ5ID0gMildLA0KICBPdXRjb21lV0VtcHR5X0lzbGFuZDEgPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoWw0KICAgIHNlcShmcm9tID0gMSwgdG8gPSBsZW5ndGgoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCksIGJ5ID0gMyldLA0KICBPdXRjb21lV0VtcHR5X0lzbGFuZDIgPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoWw0KICAgIHNlcShmcm9tID0gMSwgdG8gPSBsZW5ndGgoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCksIGJ5ID0gMyldLA0KICBPdXRjb21lV0VtcHR5X0lzbGFuZDMgPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoWw0KICAgIHNlcShmcm9tID0gMSwgdG8gPSBsZW5ndGgoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCksIGJ5ID0gMyldDQopDQoNCmlzbGFuZEludGVyYWN0aW9uUmVzdWx0cw0KYGBgDQpvcmlnaW5hbCBvdXRjb21lcyBiZWZvcmUgZml4Lg0KYGBge3IgbWF0Y2hlc30NCmlzbGFuZEludGVyYWN0aW9uUmVzdWx0cyAlPiUgZHBseXI6Om11dGF0ZSgNCiAgQzFXT0ludmFkZWQgPSBDb21tdW5pdHkxICE9IE91dGNvbWVXT0VtcHR5X0lzbGFuZDEsDQogIEMyV09JbnZhZGVkID0gQ29tbXVuaXR5MiAhPSBPdXRjb21lV09FbXB0eV9Jc2xhbmQyLA0KICBDMVdJbnZhZGVkID0gQ29tbXVuaXR5MSAhPSBPdXRjb21lV0VtcHR5X0lzbGFuZDEsDQogIEMyV0ludmFkZWQgPSBDb21tdW5pdHkyICE9IE91dGNvbWVXRW1wdHlfSXNsYW5kMywNCiAgU3RhbGVtYXRlV08gPSAhQzFXT0ludmFkZWQgJiAhQzJXT0ludmFkZWQsDQogIFN0YWxlbWF0ZVcgPSAhQzFXSW52YWRlZCAmICFDMldJbnZhZGVkLA0KICBIeWJyaWRXTyA9IEMxV09JbnZhZGVkICYgQzJXT0ludmFkZWQsDQogIEh5YnJpZFcgPSBDMVdJbnZhZGVkICYgQzJXSW52YWRlZCwNCikgJT4lIGRwbHlyOjpzZWxlY3QoLWRwbHlyOjpzdGFydHNfd2l0aCgiT3V0Y29tZSIpKQ0KYGBgDQpPdXRjb21lcyBhZnRlciBmaXguIE5vdGUgdGhlIGRpZmZlcmVuY2VzIGluIHRoZSBsYXN0IGNvbHVtbiBmb3IgZWFzaWVzdCBjb21wYXJpc29uLg0KYGBge3IgbWF0Y2hlczJ9DQppc2xhbmRJbnRlcmFjdGlvblJlc3VsdHMgJT4lIGRwbHlyOjptdXRhdGUoDQogIEMxV09JbnZhZGVkID0gQ29tbXVuaXR5MSAhPSBPdXRjb21lV09FbXB0eV9Jc2xhbmQxLA0KICBDMldPSW52YWRlZCA9IENvbW11bml0eTIgIT0gT3V0Y29tZVdPRW1wdHlfSXNsYW5kMiwNCiAgQzFXSW52YWRlZCA9IENvbW11bml0eTEgIT0gT3V0Y29tZVdFbXB0eV9Jc2xhbmQxLA0KICBDMldJbnZhZGVkID0gQ29tbXVuaXR5MiAhPSBPdXRjb21lV0VtcHR5X0lzbGFuZDMsDQogIFN0YWxlbWF0ZVdPID0gIUMxV09JbnZhZGVkICYgIUMyV09JbnZhZGVkLA0KICBTdGFsZW1hdGVXID0gIUMxV0ludmFkZWQgJiAhQzJXSW52YWRlZCwNCiAgSHlicmlkV08gPSBDMVdPSW52YWRlZCAmIEMyV09JbnZhZGVkLA0KICBIeWJyaWRXID0gQzFXSW52YWRlZCAmIEMyV0ludmFkZWQsDQopICU+JSBkcGx5cjo6c2VsZWN0KC1kcGx5cjo6c3RhcnRzX3dpdGgoIk91dGNvbWUiKSkNCmBgYA0KDQojIFNhdmUgd29ya3NwYWNlDQpgYGB7ciBzYXZlfQ0Kc2F2ZSgNCiAgY2FuZGlkYXRlRGF0YSwNCiAgaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd28sDQogIGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvV2hpY2gsDQogIGlzbGFuZEludGVyYWN0aW9uc09uZVR3bywNCiAgaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvV2hpY2gsDQogIG1hdHMsDQogIHBhcmFtRnJhbWUsDQogIHBsb3RTY2FsaW5nRGF0YSwNCiAgcG9vbHMsDQogIGZpbGUgPSAiTE0xOTk2LU51bVBvb2xDb20tUURhdC0yMDIxLTA1LlJEYXRhIg0KKQ0KYGBgDQo=